#ifndef _DICOM_DIR_TREE_H_
#define _DICOM_DIR_TREE_H_

#include <map>
#include <set>
#include <boost/scoped_ptr.hpp>
#include <gwbase/base/util.h>
#include <gwbase/base/mutex.h>
#include <gwbase/base/conf.h>
#include <gwbase/db/sqlite3_wrapper.h>
#include "dicom/file_object.h"
#include "dicom/ref_object.h"
#include "dicom/dicom_conf.h"

namespace dicom {

class DmvDB {
 public:
  DmvDB();
  ~DmvDB();

  Status Open(const std::string& path);

  bool GetSubItems(const std::string& pid, std::vector<DirObject>* dirs,
                   std::vector<FileObject>* files);

  void UpdateDir(const DirObject& d);
  void UpdateFile(const FileObject& f);

  void DeleteFile(const std::string& pid, const std::string& name);
  void DeleteFile(const std::string& local_id);
  void DeleteDir(const std::string& local_id);

  bool GetSubFileByName(const std::string& pid, const std::string& name,
                        FileObject* fo);

  bool GetSubDirByName(const std::string& pid, const std::string& name,
                       DirObject* fo);

  bool GetDirByID(const std::string& local_id, DirObject* d);
  bool GetDirByCloudId(const std::string& cloud_id, DirObject* d);

  bool GetFileByID(const std::string& local_id, FileObject* d);
  bool GetFileByCloudID(const std::string& cloud_id, FileObject* d);

  void InsertRootDirNode(const std::string& local_id,
                         const std::string& cloud_id);

  std::string GetFileCloudId(const std::string& local_id);

  void SetDirCloudId(const std::string& local_id, const std::string& cloud_id);

  void SetFileCloudId(const std::string& local_id, const std::string& cloud_id);

  typedef void* EntryLockHandle;
  EntryLockHandle LockDentry(const std::string& pid, const std::string& name);
  void UnlockDentry(EntryLockHandle el);
  void UpdateParentDirModifyTime(const std::string& pid);

  bool GetDirPath(const std::string& id, std::string* path);

 private:
  void EnsureTableCreation();

  bool GetDirsByPIDWithLock(const std::string& pid,
                            std::vector<DirObject>* dirs);
  bool GetFilesByPIDWithLock(const std::string& pid,
                             std::vector<FileObject>* files);

  void ReadDirObject(db::StmtPtr& stmt, DirObject* obj);
  void ReadFileObject(db::StmtPtr& stmt, FileObject* obj);

  void UpdateDirWithLock(const DirObject& d);
  void UpdateFileWithLock(const FileObject& f);

  bool GetDirByIDWithLock(const std::string& local_id, DirObject* d);
  bool GetFileByIDWithLock(const std::string& local_id, FileObject* d);

  bool opended_;
  db::DbPtr db_;
  Mutex mu_;

  struct EntryLock {
    std::string pid;
    std::string name;
    Mutex lock;
    int counter;
  };

  typedef std::set<EntryLock*> EntryLockList;
  EntryLockList entry_locks_;
};

typedef boost::shared_ptr<DmvDB> DmvDBPtr;

struct EntryLockGuard {
 public:
  EntryLockGuard(DmvDBPtr db, const std::string& pid, const std::string& name);
  ~EntryLockGuard();

 private:
  DmvDBPtr db_;
  DmvDB::EntryLockHandle h_;
};

class DmvDBManager {
 public:
  DmvDBPtr GetDB(const std::string& user);

  DmvDBManager(const DicomConf* conf_);
  ~DmvDBManager();

 private:
  typedef std::map<std::string, DmvDBPtr> UserDBMap;
  Mutex mu_;
  UserDBMap dbs_;
  const DicomConf* conf_;
};

DmvDBManager* NewDmvDBManager(const DicomConf* conf);
}

#endif  // _DICOM_DIR_TREE_H_
